package asteroids.entity;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

import org.apache.log4j.Logger;

import asteroids.engine.ObjectManager;
import asteroids.engine.Utils;

public class Asteroid extends GameObject implements ObjectManager<Asteroid>
{
    @SuppressWarnings("unused")
    private static Logger logger = Logger.getLogger(Asteroid.class);
    private String name = "";
    private int size;

    public static int SMALL = 2;
    public static int MEDIUM = 6;
    public static int LARGE = 14;
    public static int GIANT = 999;

    private ArrayList<Asteroid> subAsteroids;

    public Asteroid(double x, double y, int size, String name)
    {
        super(x, y, makeAsteroidShape(size), GameObject.ObjectType.ASTEROID);

        this.name = name;
        this.size = size;
        this.integrity = 1;

        subAsteroids = new ArrayList<Asteroid>();

        // bigger asteroids are easier to hit; have lower value
        value = 60;
        if( size > Asteroid.SMALL )
        {
            value = 40;
        }
        if( size > Asteroid.MEDIUM )
        {
            value = 20;
        }
        if( size > Asteroid.LARGE )
        {
            value = 10;
        }
    }

    public Asteroid(double x, double y, int size)
    {
        this(x, y, size, "" + Math.random());
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

    public int getSize()
    {
        return size;
    }

    // name is currently set to a random double value in the constructor
    public boolean equals(Asteroid other)
    {
        return name.equals(other.name);
    }

    public void add(Asteroid a)
    {
        subAsteroids.add(a);
    }

    public void addAll(ArrayList<? extends Asteroid> as)
    {
        subAsteroids.addAll(as);
    }

    /**
     * returns this Asteroid, if it's still alive, and recursively collects and
     * returns all managed Asteroids
     */
    public ArrayList<Asteroid> getObjects()
    {
        ArrayList<Asteroid> allAsteroids = new ArrayList<Asteroid>();
        if( alive )
        {
            allAsteroids.add(this);
        }
        for( Asteroid a : subAsteroids )
        {
            allAsteroids.addAll(a.getObjects());
        }
        return allAsteroids;
    }

    public void update()
    {
        if( isActive() )
        {
            super.update();
        }
        for( int i = subAsteroids.size() - 1; i >= 0; i-- )
        {
            Asteroid a = subAsteroids.get(i);
            // a.update();
            if( a.isActive() )
            {
                a.update();
            }
            else
            {
                subAsteroids.remove(i);
                a = null;
            }
        }
    }

    public boolean isActive()
    {
        return alive || subAsteroids.size() > 0 || icing.size() > 0;
    }

    @Override
    public double getCollisionPower(GameObject other)
    {
        double pow = 1;
        if( size > Asteroid.SMALL )
        {
            pow = 2;
        }
        if( size > Asteroid.MEDIUM )
        {
            pow = 3;
        }
        if( size > Asteroid.LARGE )
        {
            pow = 5;
        }
        return pow * 2;
    }

    @Override
    public void collide(GameObject other, double power)
    {
        super.collide(other, power);
        // push the other away, if it lives
        // Utils.PolarCoordinate vel =
        // Utils.polarFromCartesian(Utils.createCartesianCoordinate(
        // x_speed, y_speed));
        // other.addVelocity(Utils.createPolarCoordinate(
        // Utils.pointDirection(getXPosition(), getYPosition(),
        // other.getXPosition(),
        // other.getYPosition()), vel.getRadius()));

        if( !isAlive() )
        {
            addAll(fragment());
        }
    }

    /**
     * Generates several new Asteroids from the remains of this one and returns
     * them in an ArrayList
     * 
     * @param maxSize
     *            - the largest any individual child asteroid can be
     * @return
     */
    public ArrayList<Asteroid> fragment(int maxSize)
    {
        alive = false;
        color = Color.BLACK;
        ArrayList<Asteroid> newRocks = new ArrayList<Asteroid>();

        double radius = 0;
        for( Utils.PolarCoordinate p : objectShape )
        {
            radius += p.getRadius();
        }
        radius /= objectShape.length; // holds the average radius of this
                                      // Asteroid

        while (size > 2)
        {
            int s = (int) (Math.random() * Math.min(maxSize, (size * 2 / 3))) + 1;
            Utils.CartesianCoordinate coord = Utils.cartesianFromPolar(Utils.createPolarCoordinate(
                    Math.random() * 360 + 1, radius * 10 / Utils.FRAMES_PER_SECOND));
            Asteroid asteroid = new Asteroid(x_position + coord.getXComponent(), y_position
                    + coord.getYComponent(), s);
            asteroid.setVelocity(coord.scaleBy(Math.random()));
            asteroid.setRotationSpeed(Math.random() * (300 / Utils.FRAMES_PER_SECOND));
            asteroid.setAffiliation(affiliation);
            newRocks.add(asteroid);
            size -= s;
        }

        // add shrapnel effects.
        for( int k = 0; k < (int) (Math.round(Math.random() * 6 + 4)); k++ )
        {
            int s = (int) (Math.random() * (max_radius - 4)) + 4;
            Utils.CartesianCoordinate coord = Utils.cartesianFromPolar(Utils.createPolarCoordinate(
                    Math.random() * 360 + 1, radius / 2));
            Utils.PolarCoordinate[] icingShape = { Utils.createPolarCoordinate(0, s / 2),
                    Utils.createPolarCoordinate(180, s / 2) };
            Icing i = new Icing(x_position + coord.getXComponent(), y_position
                    + coord.getYComponent(), Utils.FRAMES_PER_SECOND, icingShape,
                    GameObject.ObjectType.ICING);
            i.setRotation(Math.random() * 360);
            i.setRotationSpeed(Math.random() * 360 / (1.5 * Utils.FRAMES_PER_SECOND) + 2.5);
            i.setVelocity(Math.random() * (100 / Utils.FRAMES_PER_SECOND)
                    - (50 / Utils.FRAMES_PER_SECOND), Math.random()
                    * (100 / Utils.FRAMES_PER_SECOND) - (50 / Utils.FRAMES_PER_SECOND));
            icing.add(i);
        }

        return newRocks;
    }

    public ArrayList<Asteroid> fragment()
    {
        return fragment(size);
    }

    private static Utils.PolarCoordinate[] makeAsteroidShape(int size)
    {
        int verts;
        int radius;

        // logger.info("Creating asteroid with size " + size);
        switch (size)
        {
            case 1:
            case 2:
                // small
                verts = 4;
                radius = 4;
                break;
            case 3:
            case 4:
            case 5:
            case 6:
                // medium
                verts = 6;
                radius = 8;
                break;
            case 7:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
            case 13:
            case 14:
                // large
                verts = 8;
                radius = 14;
                break;
            default:
                // giant
                verts = 10;
                radius = 20;
        }

        Utils.PolarCoordinate[] asteroidShape = new Utils.PolarCoordinate[verts];
        double minRotation = 360 / (verts + 2);
        double maxRotation = 360 / verts;
        double rotationRange = maxRotation - minRotation;
        double cumulativeRotation = 0;
        double radiusVariance = radius * 0.5f;

        for( int i = 0; i < verts; i++ )
        {
            asteroidShape[i] = Utils.createPolarCoordinate(Math.random() * rotationRange
                    + minRotation + cumulativeRotation, Math.random() * radiusVariance
                    - (radiusVariance / 2) + radius);
            cumulativeRotation = asteroidShape[i].getRotation();
        }

        return asteroidShape;
    }

    public void clear()
    {
        for( Asteroid a : subAsteroids )
        {
            a.clear();
        }
        subAsteroids.clear();
    }

    public void erase(Graphics g)
    {
        super.erase(g);
        for( Asteroid a : subAsteroids )
        {
            a.erase(g);
        }
    }

    public void draw(Graphics g)
    {
        // if(alive || icing.size() > 0){
        super.draw(g);
        // }
        for( Asteroid a : subAsteroids )
        {
            a.draw(g);
        }
    }
}
